﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlServerCe;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Threading;
using PetaPoco;

/// <summary>
/// Helper class to trace the application behavior
/// </summary>
public static class Log
{
    #region Nested type: ILogger
    /// <summary>
    /// The basic behaviour of any logger
    /// </summary>
    public interface ILogger
    {
        LogEntry.EntrySeverities MinSeverity { get; set; }
        bool Write(LogEntry.EntrySeverities entrySeverity, string entryMessage, LogEntry.EntryCategories entryCategory = LogEntry.EntryCategories.Base, string entrySource = "", string userName = "");
        bool WriteMessage(string message);
        string ComposeMessage(LogEntry.EntrySeverities entrySeverity, string entryMessage, LogEntry.EntryCategories entryCategory, string entrySource = "", string userName = "");
        string FormatTimeStamp(DateTime timeStamp);
        string FormatSeverity(LogEntry.EntrySeverities logSeverity);
        string FormatCategory(LogEntry.EntryCategories logCategory);
    }
    #endregion
    #region Nested type: LogEntry
    /// <summary>
    /// A formal definition for an log entry
    /// </summary>
    public class LogEntry
    {
        #region LogSeverities enum

        /// <summary>
        /// used to categorize an entry. The greater the importance
        /// </summary>
        public enum EntrySeverities
        {
            Debug,
            Info,
            Warning,
            Error,
            Fatal
        }

        #endregion

        #region LogCategories enum

        /// <summary>
        /// Used to know the subsystem that writes the entry
        /// </summary>
        public enum EntryCategories
        {
            Application,
            Base,
            Config,
            DataBase,
            Files,
            Log,
            Model,
            NetWork,
            Security,
            System,
            Test,
            Web,
            Windows,
        }

        #endregion

        public long Id { get; set; }
        public DateTime Created { get; set; }
        public EntrySeverities Severity { get; set; }
        public EntryCategories Category { get; set; }
        public string Source { get; set; }
        public string Message { get; set; }
        private string _systemData;
        public string SystemData
        {
            get
            {
                _systemData = Environment.MachineName + ", Mb:" + (Environment.WorkingSet / 1024 / 1024);
                return _systemData;
            }
            set { _systemData = value; }
        }
        private string _userName;
        public string UserName
        {
            get
            {
                // TODO: Get session user impersonated in inet web applications
                _userName = Environment.UserName;
                return _userName;
            }
            set { _userName = value; }
        }
        /// <summary>
        /// Custom constructor
        /// </summary>
        /// <param name="entrySeverity"></param>
        /// <param name="entryMessage"></param>
        /// <param name="entryCategory"></param>
        /// <param name="entrySource"></param>
        /// <returns></returns>
        public static LogEntry CreateLogEntry(EntrySeverities entrySeverity, string entryMessage, EntryCategories entryCategory, string entrySource = "", string userName = "")
        {
            return new LogEntry { Created = DateTime.Now, Severity = entrySeverity, Category = entryCategory, Message = entryMessage, Source = entrySource, UserName = userName};
        }
    }
    #endregion

    private static List<ILogger> _loggers;
    /// <summary>
    /// List of all registered loggers
    /// </summary>
    public static List<ILogger> Loggers
    {
        get
        {
            if (_loggers.IsNullOrEmpty())
            {   // TODO: Load loggers from configuration settings, using factories...
                _loggers = new List<ILogger> { new FileLogger() };
                // TODO: Inject delegates from config
                Config.SafeLogger = ConfigLogger;
            }
            return _loggers;
        }
        set { _loggers = value; }
    }
    /// <summary>
    /// Main writer method, will call each logger based on the entry Severity
    /// </summary>
    /// <param name="entrySeverity"></param>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    private static void Write(LogEntry.EntrySeverities entrySeverity, string entryMessage, LogEntry.EntryCategories entryCategory = LogEntry.EntryCategories.Base, string entrySource = "", string userName = "")
    {
        // Note: To avoid circular calls dont log the log messages
        if (entryCategory == LogEntry.EntryCategories.Log) WriteSafe(entrySeverity, entryMessage, entryCategory); 
        else
        {
            foreach (ILogger logger in Loggers)
            {
                if (entrySeverity >= logger.MinSeverity)
                {// Only a call to a logger suited for the current severity is issued
                    try
                    {
                        logger.Write(entrySeverity, entryMessage, entryCategory, entrySource);
                    }
                    catch (Exception e)
                    {
                        WriteSafe(LogEntry.EntrySeverities.Fatal, e.ToString(), LogEntry.EntryCategories.Log);
                    }
                }
            }
        }
    }
    /// <summary>
    /// Writes message with the lowest severity. Normally used at development time
    /// </summary>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    public static void Debug(string entryMessage, LogEntry.EntryCategories entryCategory = LogEntry.EntryCategories.Base, string userName = "")
    {
        Write(LogEntry.EntrySeverities.Debug, entryMessage, entryCategory);
    }
    /// <summary>
    /// Writes message with low severity. Normally used to trace the behaviour of the application
    /// </summary>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    public static void Info(string entryMessage, LogEntry.EntryCategories entryCategory = LogEntry.EntryCategories.Base, string userName = "")
    {
        Write(LogEntry.EntrySeverities.Info, entryMessage, entryCategory);
    }
    /// <summary>
    /// Writes a warning message. Is the lowest error messages. Normally indicates a condition not satisfeid, but controlled
    /// </summary>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    public static void Warning(string entryMessage, LogEntry.EntryCategories entryCategory = LogEntry.EntryCategories.Base, string userName = "")
    {
        Write(LogEntry.EntrySeverities.Warning, entryMessage, entryCategory);
    }

    
    /// <summary>
    /// Writes an error message. Normally indicates an exception caught, and allows the program to continue
    /// </summary>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    public static void Error(string entryMessage, LogEntry.EntryCategories entryCategory = LogEntry.EntryCategories.Base, string userName = "")
    {
        var callingMethod = GetCallingMethod();
        Write(LogEntry.EntrySeverities.Error, callingMethod + ": " + entryMessage, entryCategory, callingMethod);
    }
    /// <summary>
    /// Writes an error message. Normally indicates an exception caught, and allows the program to continue
    /// </summary>
    /// <param name="exception">The exception that cases the entry</param>
    /// <param name="entryCategory"></param>
    public static void Error(Exception exception, LogEntry.EntryCategories entryCategory = LogEntry.EntryCategories.Base, string userName = "")
    {
        var callingMethod = GetCallingMethod();
        Write(LogEntry.EntrySeverities.Error, exception.ToLogString(), entryCategory, callingMethod);
    }
    /// <summary>
    /// Highest severity. Exception uncaught. The program may need to restart.
    /// </summary>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    public static void Fatal(string entryMessage, LogEntry.EntryCategories entryCategory = LogEntry.EntryCategories.Base, string userName="")
    {
        var callingMethod = GetCallingMethod();
        Write(LogEntry.EntrySeverities.Fatal, entryMessage, entryCategory, callingMethod);
    }
    /// <summary>
    /// Highest severity. Exception uncaught. The program may need to restart.
    /// </summary>
    /// <param name="exception">The exception that cases the entry</param>
    /// <param name="entryCategory"></param>
    public static void Fatal(Exception exception, LogEntry.EntryCategories entryCategory = LogEntry.EntryCategories.Base, string userName = "")
    {
        Write(LogEntry.EntrySeverities.Fatal, exception.ToLogString(), entryCategory);
    }
    /// <summary>
    /// Go thru the stack to get the calling method name.
    /// </summary>
    /// <returns></returns>
    private static string GetCallingMethod()
    {
        var stack = new System.Diagnostics.StackTrace(2);
        var frame = stack.GetFrame(0);
        var method = frame.GetMethod();
        string callingMethod = method.DeclaringType + "." + method.Name;
        // TODO: Id database exception get one deeper level
        // TODO: Short names
        return callingMethod;
    }
    #region Default and safe logging, used by the Log and Config Subsystems
    /// <summary>
    /// Writes to a file in users directory
    /// </summary>
    /// <param name="entrySeverity"></param>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    private static void WriteSafe(LogEntry.EntrySeverities entrySeverity, string entryMessage, LogEntry.EntryCategories entryCategory = LogEntry.EntryCategories.Base)
    {
        try
        {
            string message = DateTime.Now + ": " + entrySeverity + ": " + entryCategory + " | " + entryMessage;
            System.Diagnostics.Trace.Write(message);
            string directory = Config.WorkDirectory + "\\Data\\Logs\\";
            if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
            string path = directory + DateTime.Now.ToString("yyyyMMdd") + ".txt";
            using (var file = new StreamWriter(path, true))
            {
                file.WriteLine(message);
                file.Close();
            }
        }
        catch (Exception exception)
        {
            System.Diagnostics.Trace.Fail(exception.ToString());
        }
    }
    /// <summary>
    /// Delegate to write config entries. 
    /// <remarks>We use a delegate to avoid config have references to log.</remarks>
    /// </summary>
    /// <param name="entryMessage"></param>
    /// <returns></returns>
    private static bool ConfigLogger(string entryMessage)
    {
        WriteSafe(LogEntry.EntrySeverities.Fatal, entryMessage, LogEntry.EntryCategories.Config);
        return true;
    }
    #endregion

    
}
/// <summary>
/// Base class to create loggers
/// </summary>
public abstract class BaseLogger : Log.ILogger
{
    #region ILogger Members
    private Log.LogEntry.EntrySeverities _minSeverity = Log.LogEntry.EntrySeverities.Debug;
    /// <summary>
    /// Minimal Severity for this Logger. Wraper for the config
    /// </summary>
    public Log.LogEntry.EntrySeverities MinSeverity
    {
        get { return _minSeverity; }
        set { _minSeverity = value; }
    }
    /// <summary>
    /// Basic writer method. 
    /// <remarks> Will retry whe writing whe fails</remarks>
    /// </summary>
    /// <param name="entrySeverity"></param>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    /// <returns></returns>
    public virtual bool Write(Log.LogEntry.EntrySeverities entrySeverity, string entryMessage, Log.LogEntry.EntryCategories entryCategory = Log.LogEntry.EntryCategories.Base, string source = "", string userName = "")
    {
        string message = ComposeMessage(entrySeverity, entryMessage, entryCategory);
        return WriteMessage(message); ;
    }
    /// <summary>
    /// Basic message composer
    /// </summary>
    /// <param name="entrySeverity"></param>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    /// <returns></returns>
    public virtual string ComposeMessage(Log.LogEntry.EntrySeverities entrySeverity, string entryMessage, Log.LogEntry.EntryCategories entryCategory, string source = "", string userName = "")
    {
        Log.LogEntry logEntry = Log.LogEntry.CreateLogEntry(entrySeverity, entryMessage, entryCategory);
        string template = string.Format("{0} | {1} | {2} | {3} | {4} | {5}", FormatTimeStamp(logEntry.Created), FormatSeverity(logEntry.Severity).FixedLenght(10), FormatCategory(logEntry.Category).FixedLenght(10), Thread.CurrentThread.ManagedThreadId.ToString().FixedLenght(2),
                                        logEntry.Source.FixedLenght(20), logEntry.Message);
        return template;
    }
    /// <summary>
    /// The real writer, empty by default
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    public virtual bool WriteMessage(string message)
    {
        return true;
    }
    #region formatters
    public virtual string FormatTimeStamp(DateTime timeStamp)
    {
        return timeStamp.ToString("HH:mm:ss");
    }
    public virtual string FormatSeverity(Log.LogEntry.EntrySeverities logSeverity)
    {
        switch (logSeverity)
        {
            case Log.LogEntry.EntrySeverities.Debug:
                return "Debug:";
            case Log.LogEntry.EntrySeverities.Info:
                return "Info:";
            case Log.LogEntry.EntrySeverities.Warning:
                return "Warnig:";
            case Log.LogEntry.EntrySeverities.Error:
                return "Error:";
            case Log.LogEntry.EntrySeverities.Fatal:
                return "FATAL:";
            default:
                return "";
        }
    }
    public virtual string FormatCategory(Log.LogEntry.EntryCategories logCategory)
    {
        if (logCategory == Log.LogEntry.EntryCategories.Base)
            return "";
        else
            return logCategory.ToString();
    }
    #endregion
    #endregion
}
/// <summary>
/// Logger that writes entries to a file
/// Works asynchronously
/// </summary>
public class FileLogger : BaseLogger
{
    /// <summary>
    /// Directory to write logs
    /// <add key="LogsDirectory" value="\\Data\\Logs\\"/>
    /// </summary>
    /// <returns></returns>
    public static string LogsDirectory
    {
        get { return Config.GetValue<string>("FileLogger.LogsDirectory", @"\\Data\\Logs\\",false); }
        set { Config.SetValue("FileLogger.LogsDirectory", value,false); }
    }


    /// <summary>
    /// <add key="MaxLogSizeKb" value="1024"/>
    /// </summary>
    /// <returns></returns>
    public static int MaxLogSizeKb
    {
        get { return Config.GetValue<int>("FileLogger.MaxLogSizeKb", 1024, false); }
        set { Config.SetValue("FileLogger.MaxLogSizeKb", value, false); }
    }

    private StreamWriter _writer;
    /// <summary>
    /// The writer used to access the file
    /// </summary>
    public StreamWriter Writer
    {
        get
        {
            if (MaxLogSizeKb > 0 && LastSize >= MaxLogSizeKb * 1024)
            {
                if (_writer != null) _writer.Close();
                File.Move(FileName, FileBackupName);
                LastSize = 0;
            }
            var fileStream = new FileStream(FileName, FileMode.Append);
            _writer = new StreamWriter(fileStream);
            return _writer;
        }
        set { _writer = value; }
    }

    private String _fileName;
    /// <summary>
    /// The name of the file to which this Logger is writing.
    /// </summary>
    private string FileName
    {
        get
        {
            if (_fileName == null)
                _fileName = string.Format("{0} {1} {2} {3}", DirectoryName, Config.ApplicationName, TimeStamp, ".log");
            return _fileName;
        }
    }

    private string _directoryName;
    /// <summary>
    /// Name of the Directory where we will create files
    /// </summary>
    private string DirectoryName
    {
        get
        {
            if (_directoryName == null)
            {
                string directoryConfig = LogsDirectory;
                // TODO: check for relative versus absolute paths and dir separators
                _directoryName = Config.WorkDirectory + directoryConfig;
                if (!Directory.Exists(_directoryName))
                    Directory.CreateDirectory(_directoryName);
            }
            return _directoryName;
        }
    }
    
    /// <summary>
    /// Date part for the loging file name
    /// </summary>
    private static string TimeStamp
    {
        get { return DateTime.Now.ToString("yyyyMMdd"); }
    }
    /// <summary>
    /// Creates new name based on the current time (second)
    /// </summary>
    /// <value></value>
    private string FileBackupName
    {
        get { return DirectoryName + Path.GetFileNameWithoutExtension(FileName) + "." + DateTime.Now.ToString("HHmmss"); }
    }

    /// <summary>
    /// current file size
    /// </summary>
    public static long LastSize { get; private set; }

    /// <summary>
    /// Write the message to the file asynchronously.
    /// </summary>
    /// <param name="message">The String representing the LogEntry being logged.</param>
    /// <returns></returns>
    public override bool WriteMessage(String message)
    {
        ThreadPool.QueueUserWorkItem(DoWriteMessage, message);
        return true;
    }
    /// <summary>
    /// Writes the message to the file
    /// </summary>
    /// <param name="message"></param>
    private void DoWriteMessage(object message)
    {
        // We need to lock the execution to prevent multiple threads working on the same file
        lock (this)
        {
            StreamWriter writer = null;
            try
            {
                writer = Writer;
                writer.WriteLine(message);
                LastSize = writer.BaseStream.Length;
            }
            finally
            {
                try
                {// Never leave opened a stream 
                    if (writer != null) writer.Close();
                }
                catch (Exception)
                {// Never leave a finally block without protection
                }
            }
        }
    }
    /// <summary>
    /// Formatter to generate notepad++ smalltalk tags to have a colorized log ;-)
    /// </summary>
    /// <param name="logSeverity"></param>
    /// <returns></returns>
    public override string FormatSeverity(Log.LogEntry.EntrySeverities logSeverity)
    {
        switch (logSeverity)
        {
            case Log.LogEntry.EntrySeverities.Debug:
                return "'dbg'".FixedLenght(7);
            case Log.LogEntry.EntrySeverities.Info:
                return "inf:".FixedLenght(7);
            case Log.LogEntry.EntrySeverities.Warning:
                return "#Wrng".FixedLenght(7);
            case Log.LogEntry.EntrySeverities.Error:
                return "\"Error\"".FixedLenght(7);
            case Log.LogEntry.EntrySeverities.Fatal:
                return "\"FATAL\"".FixedLenght(7);
            default:
                return "";
        }
    }
}
/// <summary>
/// Logger that sends messages by email
/// </summary>
public class MailLogger : BaseLogger
{
    /// <summary>
    /// <add key="MailServer" value="smtp.live.com"/>
    /// </summary>
    /// <returns></returns>
    public static string MailServer
    {
        get { return Config.GetValue("MailLogger.MailServer", @"smtp.live.com"); }
        set { Config.SetValue("MailLogger.MailServer", value); }
    }
    /// <summary>
    /// <add key="LogMailSender" value="albertobasalo@hotmail.com"/>
    /// </summary>
    /// <returns></returns>
    public static string LogMailSender
    {
        get { return Config.GetValue("MailLogger.LogMailSender", @"albertobasalo@hotmail.com"); }
        set { Config.SetValue("MailLogger.LogMailSender", value); }
    }
    /// <summary>
    /// <add key="LogMailDestination" value="support@agorabinaria.com"/>
    /// </summary>
    /// <returns></returns>
    public static string LogMailDestination
    {
        get { return Config.GetValue("MailLogger.LogMailDestination", @"albertobasalo@hotmail.com"); }
        set { Config.SetValue("MailLogger.LogMailDestination", value); }
    }
    /// <summary>
    /// Cero based retries when log fails writing
    /// <add key="MaxAttempts" value="1"/>
    /// </summary>
    /// <returns></returns>
    public static byte MaxAttempts
    {
        get { return Config.GetValue<byte>("MailLogger.MaxAttempts", 1); }
        set { Config.SetValue("MailLogger.MaxAttempts", value); }
    }

    /// <summary>
    /// Constructor
    /// </summary>
    public MailLogger()
    {
        // Only entries with Warning or more severiry can use this logger by default
        MinSeverity = Log.LogEntry.EntrySeverities.Warning;
    }

    /// <summary>
    /// Basic writer method. 
    /// <remarks> Will retry whe writing whe fails</remarks>
    /// </summary>
    /// <param name="entrySeverity"></param>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    /// <returns></returns>
    public override bool Write(Log.LogEntry.EntrySeverities entrySeverity, string entryMessage, Log.LogEntry.EntryCategories entryCategory = Log.LogEntry.EntryCategories.Base, string source = "", string userName = "")
    {
        string message = ComposeMessage(entrySeverity, entryMessage, entryCategory);
        int attempts = 0;
        do
        {
            attempts++;
            try
            {
                WriteMessage(message);
                return true;
            }
            catch (Exception e)
            {
                Log.Error(e.ToString(), Log.LogEntry.EntryCategories.Log);
            }
        } while (attempts < MaxAttempts);
        return false;
    }

    private string _subject;
    /// <summary>
    /// Composes the subject a the body of the email generated for this entry
    /// </summary>
    /// <param name="entrySeverity"></param>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    /// <returns></returns>
    public override string ComposeMessage(Log.LogEntry.EntrySeverities entrySeverity, string entryMessage, Log.LogEntry.EntryCategories entryCategory, string entrySource = "", string userName = "")
    {
        Log.LogEntry logEntry = Log.LogEntry.CreateLogEntry(entrySeverity, entryMessage, entryCategory);
        _subject = string.Format("{0} {1} ", FormatSeverity(logEntry.Severity), FormatCategory(logEntry.Category));
        string newLine = Environment.NewLine + " - ";
        // Format YAML message
        string template = string.Format("{0}: from {1} at {2} {3}  --{4} {5}  --{6} {7} --{8}",
                                        FormatSeverity(logEntry.Severity), FormatCategory(logEntry.Category), FormatTimeStamp(logEntry.Created),
                                        newLine, logEntry.Message,
                                        newLine, logEntry.Source,
                                        newLine, logEntry.SystemData);
        return template;
    }
    /// <summary>
    /// Sends the message by email
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    public override bool WriteMessage(string message)
    {
        var smtpClient = new SmtpClient(MailServer);
        smtpClient.Credentials = new NetworkCredential(LogMailSender, password: "myPassword");
        smtpClient.EnableSsl = true;
        smtpClient.Send(LogMailSender, LogMailDestination, _subject, message);
        return true;
    }
}
/// <summary>
/// Logger that writes to the main console
/// </summary>
public class ConsoleLogger : BaseLogger
{
    public ConsoleLogger()
    {
        base.MinSeverity = Log.LogEntry.EntrySeverities.Info;
    }

    /// <summary>
    /// Write once 
    /// </summary>
    /// <param name="entrySeverity"></param>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    /// <returns></returns>
    public override bool Write(Log.LogEntry.EntrySeverities entrySeverity, string entryMessage, Log.LogEntry.EntryCategories entryCategory = Log.LogEntry.EntryCategories.Base, string entrySource = "", string userName = "")
    {
        string message = ComposeMessage(entrySeverity, entryMessage, entryCategory, entrySource);
        WriteMessage(message);
        return true;
    }

    public override string ComposeMessage(Log.LogEntry.EntrySeverities entrySeverity, string entryMessage, Log.LogEntry.EntryCategories entryCategory, string entrySource, string userName = "")
    {
        Log.LogEntry logEntry = Log.LogEntry.CreateLogEntry(entrySeverity, entryMessage, entryCategory);
        string template = string.Format("{0} {1} {2} ", FormatSeverity(logEntry.Severity), FormatCategory(logEntry.Category), logEntry.Message);
        return template;
    }

    /// <summary>
    /// Writes a string to the console
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    public override bool WriteMessage(string message)
    {
        Console.WriteLine(message);
        return true;
    }
}
/// <summary>
/// Logger that writes to the lessFramework DataBase 
/// </summary>
public class DatabaseLogger : BaseLogger
{
    /// <summary>
    /// <add key="TableName" value="LogEntries"/>
    /// </summary>
    /// <returns></returns>
    public static string TableName
    {
        get { return Config.GetValue<string>("DatabaseLogger.TableName", "LogEntries", false); }
        set { Config.SetValue("DatabaseLogger.TableName", value, false); }
    }

    /// <summary>
    /// <add key="IdColumnName" value="Id"/>
    /// </summary>
    /// <returns></returns>
    public static string IdColumnName
    {
        get { return Config.GetValue<string>("DatabaseLogger.IdColumnName", "Id", false); }
        set { Config.SetValue("DatabaseLogger.IdColumnName", value, false); }
    }

    /// <summary>
    /// <add key="ExistsTable" value="SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'LogEntries' "/>
    /// </summary>
    /// <returns></returns>
    public static string ExistsTable
    {
        get { return Config.GetValue<string>("DatabaseLogger.ExistsTable", "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'LogEntries' ", false); }
        set { Config.SetValue("DatabaseLogger.ExistsTable", value, false); }
    }

    /// <summary>
    /// <add key="CreateTable" value="CREATE TABLE LogEntries (Id bigint IDENTITY(1,1) PRIMARY KEY,Created datetime,Severity nvarchar(20),Category nvarchar(20),Source nvarchar(4000),Message nvarchar(4000),SystemData nvarchar(4000),UserName nvarchar(20))"/>
    /// </summary>
    /// <returns></returns>
    public static string CreateTable
    {
        get { return Config.GetValue<string>("DatabaseLogger.CreateTable", @"CREATE TABLE LogEntries (  Id bigint IDENTITY(1,1) PRIMARY KEY,  Created datetime,  Severity tinyint,  Category tinyint,  Source nvarchar(4000),  Message nvarchar(4000),  SystemData nvarchar(4000),  UserName nvarchar(20))", false); }
        set { Config.SetValue("DatabaseLogger.CreateTable", value, false); }
    }

    /// <summary>
    /// <add key="DeleteOldRows" value="DELETE FROM LogEntries WHERE Datediff(day,Created,GETDATE()) > @0  "/>
    /// </summary>
    /// <returns></returns>
    public string DeleteOldRows
    {
        // TODO: Parametrize number of days 
        get { return Config.GetValue<string>("DatabaseLogger.DeleteOldRows", "DELETE FROM LogEntries WHERE Datediff(day,Created,GETDATE()) > @0 ", false); }
        set { Config.SetValue("DatabaseLogger.DeleteOldRows", value, false); }
    }

    /// <summary>
    /// <add key="DaysSaved" value="7"/>
    /// </summary>
    /// <returns></returns>
    public byte DaysSaved
    {
        get { return Config.GetValue<byte>("DatabaseLogger.DaysSaved", 7, false); }
        set { Config.SetValue("DatabaseLogger.DaysSaved", value, false); }
    }


    public static object token = new object();
    private static bool _tableReady = false;
    public static bool TableReady
    {
        get
        {
            lock (token)
            {
                if (_tableReady == false)
                    _tableReady = PrepareTable();
                return _tableReady;
            }
        }
        set { _tableReady = value; }
    }

    private static bool PrepareTable()
    {
        bool tableOk = false;
        try
        {
            var executeScalar = Data.Database.ExecuteScalar<object>(ExistsTable);
            if (executeScalar == null)
            {
                Log.Warning(string.Format("Table {0} Does not exists", TableName), Log.LogEntry.EntryCategories.DataBase);
                Data.Database.Execute(CreateTable);
                Log.Info(string.Format("Table {0} Created", TableName), Log.LogEntry.EntryCategories.DataBase);
            }
            else
            {
                Log.Debug(string.Format("Table {0} Already Exists", TableName), Log.LogEntry.EntryCategories.DataBase);
            }
            tableOk = true;
        }
        catch (Exception exception)
        {
            Log.Error(exception);
        }
        return tableOk;
    }

    public DatabaseLogger()
    {
        base.MinSeverity = Log.LogEntry.EntrySeverities.Info;
        PrepareTable();
    }

    /// <summary>
    /// DataBase writer method. 
    /// </summary>
    /// <param name="entrySeverity"></param>
    /// <param name="entryMessage"></param>
    /// <param name="entryCategory"></param>
    /// <returns></returns>
    public override bool Write(Log.LogEntry.EntrySeverities entrySeverity, string entryMessage, Log.LogEntry.EntryCategories entryCategory = Log.LogEntry.EntryCategories.Base, string source = "", string userName = "")
    {
        Log.LogEntry logEntry = Log.LogEntry.CreateLogEntry(entrySeverity, entryMessage, entryCategory, source);
        if (TableReady)
        {
            // Inserts LogEntries just like any other POCO class
            Data.Database.Insert(TableName, IdColumnName, logEntry);
            return true;
        }
        else
        {
            return false;
        }
    }
}